package excel

import (
	"context"
	"io"
	"reflect"
	"strings"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/xuri/excelize/v2"
)

// ImporterOption 导入器配置选项
type ImporterOption func(*GenericImporter)

// FieldMapper 字段映射函数
type FieldMapper func(string) string

// ValueValidator 值验证函数
type ValueValidator func(interface{}) error

// ValueTransformer 值转换函数
type ValueTransformer func(interface{}) (interface{}, error)

// ImportError 导入错误信息
type ImportError struct {
	Row    int     // 行号(从1开始)
	Errors []error // 错误列表
}

// GenericImporter 通用Excel导入器
type GenericImporter struct {
	sheetName      string
	headerRow      int
	startRow       int
	fieldMap       map[string]string // Excel列名->结构体字段名
	validators     map[string]ValueValidator
	transformers   map[string]ValueTransformer
	requiredFields map[string]bool
}

// NewGenericImporter 创建通用导入器
func NewGenericImporter(opts ...ImporterOption) *GenericImporter {
	importer := &GenericImporter{
		sheetName:      "Sheet1",
		headerRow:      1,
		startRow:       2,
		fieldMap:       make(map[string]string),
		validators:     make(map[string]ValueValidator),
		transformers:   make(map[string]ValueTransformer),
		requiredFields: make(map[string]bool),
	}

	for _, opt := range opts {
		opt(importer)
	}

	return importer
}

// WithSheetName 设置工作表名
func WithSheetName(name string) ImporterOption {
	return func(i *GenericImporter) {
		i.sheetName = name
	}
}

// WithHeaderRow 设置标题行号(从1开始)
func WithHeaderRow(row int) ImporterOption {
	return func(i *GenericImporter) {
		i.headerRow = row
	}
}

// WithStartRow 设置数据起始行(从1开始)
func WithStartRow(row int) ImporterOption {
	return func(i *GenericImporter) {
		i.startRow = row
	}
}

// WithFieldMap 设置字段映射(Excel列名->结构体字段名)
func WithFieldMap(m map[string]string) ImporterOption {
	return func(i *GenericImporter) {
		for k, v := range m {
			i.fieldMap[k] = v
		}
	}
}

// WithValidator 设置字段验证器
func WithValidator(field string, validator ValueValidator) ImporterOption {
	return func(i *GenericImporter) {
		i.validators[field] = validator
	}
}

// WithTransformer 设置字段转换器
func WithTransformer(field string, transformer ValueTransformer) ImporterOption {
	return func(i *GenericImporter) {
		i.transformers[field] = transformer
	}
}

// WithRequiredFields 设置必填字段
func WithRequiredFields(fields ...string) ImporterOption {
	return func(i *GenericImporter) {
		for _, field := range fields {
			i.requiredFields[field] = true
		}
	}
}

// Import 执行导入操作
// Import 执行导入操作
func (i *GenericImporter) Import(ctx context.Context, file io.Reader, result interface{}) ([]ImportError, error) {
	// 1. 打开Excel文件
	f, err := excelize.OpenReader(file)
	if err != nil {
		return nil, gerror.Wrap(err, "打开Excel文件失败")
	}
	defer f.Close()

	// 2. 获取表头映射
	headerMap, err := i.parseHeader(f)
	if err != nil {
		return nil, gerror.Wrap(err, "解析表头失败")
	}

	// 3. 准备结果容器 - 修复反射操作
	resultValue := reflect.ValueOf(result)
	if resultValue.Kind() != reflect.Ptr {
		return nil, gerror.New("结果参数必须是指针")
	}

	sliceValue := resultValue.Elem()
	if sliceValue.Kind() != reflect.Slice {
		return nil, gerror.New("结果参数必须指向切片")
	}

	elementType := sliceValue.Type().Elem()

	// 确保元素类型是结构体或结构体指针
	if elementType.Kind() != reflect.Struct &&
		!(elementType.Kind() == reflect.Ptr && elementType.Elem().Kind() == reflect.Struct) {
		return nil, gerror.New("切片元素必须是结构体或结构体指针")
	}

	// 4. 读取并处理数据行
	rows, err := f.GetRows(i.sheetName)
	if err != nil {
		return nil, gerror.Wrapf(err, "获取工作表[%s]行数据失败", i.sheetName)
	}

	var importErrors []ImportError
	for rowNum := i.startRow; rowNum <= len(rows); rowNum++ {
		row := rows[rowNum-1]

		// 跳过空行
		if i.isEmptyRow(row) {
			continue
		}

		rowErrors := i.processRow(ctx, rowNum, row, headerMap, elementType)
		if len(rowErrors) > 0 {
			importErrors = append(importErrors, ImportError{
				Row:    rowNum,
				Errors: rowErrors,
			})
			continue
		}

		// 创建新元素并填充数据
		elem := reflect.New(elementType).Elem()
		if err := i.fillElement(elem, row, headerMap); err != nil {
			importErrors = append(importErrors, ImportError{
				Row:    rowNum,
				Errors: []error{err},
			})
			continue
		}

		sliceValue.Set(reflect.Append(sliceValue, elem))
	}

	return importErrors, nil
}

// parseHeader 解析表头，建立字段映射
func (i *GenericImporter) parseHeader(f *excelize.File) (map[string]string, error) {
	headerMap := make(map[string]string)

	rows, err := f.GetRows(i.sheetName)
	if err != nil {
		return nil, err
	}

	if len(rows) < i.headerRow {
		return nil, gerror.Newf("工作表[%s]没有足够的行", i.sheetName)
	}

	headers := rows[i.headerRow-1]
	for colIdx, header := range headers {
		if field, ok := i.fieldMap[header]; ok {
			colName := columnIndexToLetters(colIdx)
			headerMap[field] = colName
		}
	}

	// 检查必填字段
	for field, required := range i.requiredFields {
		if required {
			found := false
			for _, v := range i.fieldMap {
				if v == field {
					found = true
					break
				}
			}
			if !found {
				return nil, gerror.Newf("必填字段[%s]在Excel中缺失", field)
			}
		}
	}

	return headerMap, nil
}

// processRow 处理单行数据，进行基本验证
func (i *GenericImporter) processRow(ctx context.Context, rowNum int, row []string, headerMap map[string]string, elementType reflect.Type) []error {
	var errors []error

	// 检查必填字段
	for field, required := range i.requiredFields {
		if required {
			if col, ok := headerMap[field]; ok {
				colIdx := lettersToColumnIndex(col)
				if colIdx >= len(row) || row[colIdx] == "" {
					errors = append(errors, gerror.Newf("字段[%s]是必填的", field))
				}
			}
		}
	}

	return errors
}

// fillElement 填充元素数据 - 修复反射操作
func (i *GenericImporter) fillElement(elem reflect.Value, row []string, headerMap map[string]string) error {
	// 处理指针类型
	if elem.Kind() == reflect.Ptr {
		if elem.IsNil() {
			// 如果是nil指针，创建新的结构体
			elem.Set(reflect.New(elem.Type().Elem()))
		}
		elem = elem.Elem()
	}

	// 确保是结构体类型
	if elem.Kind() != reflect.Struct {
		return gerror.New("元素必须是结构体类型")
	}

	for field, col := range headerMap {
		colIdx := lettersToColumnIndex(col)
		if colIdx >= len(row) {
			continue
		}

		value := strings.TrimSpace(row[colIdx])
		fieldValue := elem.FieldByName(field)

		// 检查字段是否存在
		if !fieldValue.IsValid() {
			// 尝试处理嵌套字段 (如 User.Name)
			if err := i.setNestedField(elem, field, value); err != nil {
				return err
			}
			continue
		}

		// 检查字段是否可设置
		if !fieldValue.CanSet() {
			return gerror.Newf("字段[%s]不可设置", field)
		}

		// 应用转换器
		if transformer, ok := i.transformers[field]; ok {
			transformed, err := transformer(value)
			if err != nil {
				return gerror.Wrapf(err, "字段[%s]转换失败", field)
			}
			value = gconv.String(transformed)
		}

		// 应用验证器
		if validator, ok := i.validators[field]; ok {
			if err := validator(value); err != nil {
				return gerror.Wrapf(err, "字段[%s]验证失败", field)
			}
		}

		// 设置值
		if err := gconv.Scan(value, fieldValue.Addr().Interface()); err != nil {
			return gerror.Wrapf(err, "字段[%s]赋值失败", field)
		}
	}

	return nil
}

// setNestedField 设置嵌套字段
func (i *GenericImporter) setNestedField(elem reflect.Value, fieldPath string, value string) error {
	fields := strings.Split(fieldPath, ".")
	current := elem

	for j, field := range fields {
		if current.Kind() == reflect.Ptr {
			if current.IsNil() {
				current.Set(reflect.New(current.Type().Elem()))
			}
			current = current.Elem()
		}

		if current.Kind() != reflect.Struct {
			return gerror.Newf("字段路径[%s]无效", fieldPath)
		}

		fieldValue := current.FieldByName(field)
		if !fieldValue.IsValid() {
			return gerror.Newf("字段[%s]不存在", field)
		}

		if j == len(fields)-1 {
			// 最后一个字段，设置值
			if err := gconv.Scan(value, fieldValue.Addr().Interface()); err != nil {
				return gerror.Wrapf(err, "字段[%s]赋值失败", fieldPath)
			}
		} else {
			// 中间字段，继续深入
			current = fieldValue
		}
	}

	return nil
}

// isEmptyRow 检查是否为空行
func (i *GenericImporter) isEmptyRow(row []string) bool {
	for _, cell := range row {
		if strings.TrimSpace(cell) != "" {
			return false
		}
	}
	return true
}

// columnIndexToLetters 将列索引转换为字母表示 (0 -> A, 1 -> B, ..., 26 -> AA)
func columnIndexToLetters(col int) string {
	var letters string
	for col >= 0 {
		letters = string(rune(col%26+'A')) + letters
		col = col/26 - 1
	}
	return letters
}

// lettersToColumnIndex 将字母表示转换为列索引 (A -> 0, B -> 1, ..., AA -> 26)
func lettersToColumnIndex(letters string) int {
	letters = strings.ToUpper(letters)
	var col int
	for _, r := range letters {
		col = col*26 + int(r) - 'A' + 1
	}
	return col - 1
}
